home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
X11
/
xarchie-2.0.9
/
dirsend.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-06-18
|
34KB
|
1,239 lines
/*
* Copyright (c) 1989, 1990, 1991 by the University of Washington
*
* For copying and distribution information, please see the file
* <copyright.h>.
*
* xarchie v2.0 - (gf) - Sync with archie-1.4.1 (uses config.h)
* v1.2.3 - 11/04/91 (bcn) - removed host comparison and replaced with check
for connection id (undoes effect of v1.2.2.).
* v1.2.2 - 11/02/91 (gf) - removed extra inet_ntoa() calls and stuff for
multi-interface nets (lmjm@doc.imperial.ac.uk)
* v1.2.1 - 10/20/91 (gf) - asynch implementation
* v1.2.0 - 09/17/91 (bpk) - added BULL & USG stuff, thanks to Jim Sillas
* v1.1.2 - 08/30/91 (bpk) - added VMS support
* v1.1.1 - 08/29/91 (bcn) - changed backoff handling
* v1.1.0 - 08/13/91 (gf) - added XArchie status calls
*
* gf: 20 Oct 1991:
* Broken into pieces so that under X dirsend() doesn't block in select()
* but rather uses Xt calls to allow continued event processing. If
* XARCHIE is not defined, can still be used since processEvent() will
* use select() in this case.
*
* bpk: For archie client v1.3.2:
* If you're going to hack on this, I'd suggest using unifdef with -UCUTCP
* and possibly -UVMS, for your working copy. When you've got your changes
* done, come back and add them into this main file. It's getting pretty
* nasty down there.
*/
#include <copyright.h>
#include <stdio.h>
#include <errno.h>
/*
* Complaints or suggestions regarding the portability or lack thereof
* of these includes and defines should be directed to Brendan Kehoe,
* brendan@cs.widener.edu.
*/
#ifdef VMS
# ifdef WOLLONGONG
# include "twg$tcp:[netdist.include]netdb.h"
# else /* not Wollongong */
# ifdef UCX
# include <netdb.h>
# else /* Multinet */
# include "multinet_root:[multinet.include]netdb.h"
# endif
# endif
# include <vms.h>
#else /* not VMS */
# ifdef PCNFS
# include <tklib.h>
# include <tk_errno.h>
# include <sys/nfs_time.h>
# endif
# include "config.h" /* gf */
# include "stringdefs.h" /* gf */
# include "selectdefs.h" /* gf */
# include <sys/time.h> /* gf */
# ifdef CUTCP
# include <msdos/cutcp.h>
# include <msdos/netevent.h>
# include <msdos/hostform.h>
# else /* not CUTCP */
# include <netdb.h>
# include <sys/socket.h>
# endif
# ifndef IN_H
# ifndef _TYPES_
# include <sys/types.h>
# endif
# include <netinet/in.h>
# define IN_H
# endif
# if !defined(hpux) && !defined(PCNFS)
# include <arpa/inet.h>
# endif
#endif /* !VMS */
/* Interactive UNIX keeps some of the socket definitions in funny places. */
#ifdef ISC
# include <net/errno.h>
#endif /* ISC */
/* PC-NFS Toolkit 4.0 keeps important forward definitions here. */
#ifdef PCNFS
# include <in_addr.h>
#endif
#include <pfs.h>
#include <pprot.h>
#include <pcompat.h>
#include <perrno.h>
/* gf: Removed SUN_GNU_FIX stuff since inet_ntoa.c is now included. */
static int notprived = 0;
#ifndef MSDOS
extern int errno;
#endif
extern int perrno;
#ifdef DEBUG
extern int pfs_debug;
#endif
extern int pfs_disable_flag;
char *nlsindex();
#define max(X, Y) ((X) > (Y) ? (X) : (Y))
static int dir_udp_port = 0; /* Remote UDP port number */
#ifdef CUTCP
# define NS_TIMEOUT 15
#endif
static unsigned short next_conn_id = 0;
/* Always needed externally */
int client_dirsrv_timeout = CLIENT_DIRSRV_TIMEOUT;
int client_dirsrv_retry = CLIENT_DIRSRV_RETRY;
int rdgram_priority = 0; /* gf: was in rdgram.h */
/* These were parameters to dirsend() */
static PTEXT pkt;
static char *hostname;
static struct sockaddr_in *hostaddr;
/* These were locals in dirsend(). Note that the initializations here
* are really meaningless since we have to redo them for each call to
* dirsend() since they were formerly automatically initialized.
*/
static PTEXT first = NULL; /* First returned packet */
static PTEXT next; /* The one we are waiting for */
static PTEXT vtmp; /* For reorganizing linked list */
static PTEXT comp_thru; /* We have all packets though */
static int lp = -1; /* Opened UDP port */
static int hdr_len; /* Header Length */
static int nd_pkts; /* Number of packets we want */
static int no_pkts; /* Number of packets we have */
static int pkt_cid; /* Packet connection identifier */
static unsigned short this_conn_id; /* Connection ID we are using */
static unsigned short recvd_thru; /* Received through */
static short priority; /* Priority for request */
static short one = 0; /* Pointer to value 1 */
static short zero = 0; /* Pointer to value 0 */
static char *seqtxt; /* Pointer to text w/ sequence # */
static struct sockaddr_in us; /* Our address */
static struct sockaddr_in to; /* Address to send query */
static struct sockaddr_in from; /* Reply received from */
static int from_sz; /* Size of from structure */
static struct hostent *host; /* Host info from gethostbyname */
static long newhostaddr; /* New host address from *host */
static int req_udp_port=0; /* Requested port (optional) */
static char *openparen; /* Delimits port in name */
static char hostnoport[500];/* Host name without port */
static int ns; /* Number of bytes actually sent */
static int nr; /* Number of bytes received */
static fd_set readfds; /* Used for select */
static int tmp;
static char *ctlptr; /* Pointer to control field */
static short stmp; /* Temp short for conversions */
static int backoff; /* Server requested backoff */
static unsigned char rdflag11; /* First byte of flags (bit vect)*/
static unsigned char rdflag12; /* Second byte of flags (int) */
static int scpflag = 0; /* Set if any sequencd cont pkts */
static int ackpend = 0; /* Acknowledgement pending */
static int gaps = 0; /* Gaps present in recvd pkts */
static struct timeval timeout; /* Time to wait for response */
static struct timeval ackwait; /* Time to wait before acking */
static struct timeval gapwait; /* Time to wait b4 filling gaps */
static struct timeval *selwait; /* Time to wait for select */
static int retries; /* was = client_dirsrv_retry */
char to_hostname[512]; /* lmjm: saves inet_ntoa() str */
/* These are added so dirsend() "blocks" properly */
static PTEXT dirsendReturn;
static int dirsendDone;
/* And here are the values for dirsendDone */
#define DSRET_DONE 1
#define DSRET_SEND_ERROR -1
#define DSRET_RECV_ERROR -2
#define DSRET_SELECT_ERROR -3
#define DSRET_TIMEOUT -4
#define DSRET_ABORTED -5
/* New procedures to break up dirsend() */
static int initDirsend();
static void retryDirsend(), keepWaitingDirsend();
static void timeoutProc();
static void readProc();
/* Wrappers around X calls to allow non-X usage */
static void addInputSource(), removeInputSource();
static void addTimeOut(), removeTimeOut();
static void processEvent();
/* External status procedures */
extern void status0(),status1(),status2();
#if defined(XARCHIE) || defined(VARCHIE)
static int packetCounter;
#endif
/* Extra stuff for the asynchronous X version of dirsend() */
#ifdef XARCHIE
#include <X11/Intrinsic.h>
extern XtAppContext appContext;
#else
#ifdef VARCHIE
#include "xtypes.h"
#else
typedef char *XtPointer;
typedef char *XtInputId;
typedef char *XtIntervalId;
#endif /* VARCHIE */
#endif /* XARCHIE */
static XtInputId inputId;
static XtIntervalId timerId = (XtIntervalId)0;
/*
* dirsend - send packet and receive response
*
* DIRSEND takes a pointer to a structure of type PTEXT, a hostname,
* and a pointer to a host address. It then sends the supplied
* packet off to the directory server on the specified host. If
* hostaddr points to a valid address, that address is used. Otherwise,
* the hostname is looked up to obtain the address. If hostaddr is a
* non-null pointer to a 0 address, then the address will be replaced
* with that found in the hostname lookup.
*
* DIRSEND will wait for a response and retry an appropriate
* number of times as defined by timeout and retries (both static
* variables). It will collect however many packets form the reply, and
* return them in a structure (or structures) of type PTEXT.
*
* DIRSEND will free the packet that it is presented as an argument.
* The packet is freed even if dirsend fails.
*/
PTEXT
dirsend(pkt_p,hostname_p,hostaddr_p)
PTEXT pkt_p;
char *hostname_p;
struct sockaddr_in *hostaddr_p;
{
/* copy parameters to globals since other routines use them */
pkt = pkt_p;
hostname = hostname_p;
hostaddr = hostaddr_p;
/* Do the initializations of formerly auto variables */
first = NULL;
lp = -1;
one = 0;
zero = 0;
req_udp_port=0;
scpflag = 0;
ackpend = 0;
gaps = 0;
retries = client_dirsrv_retry;
if (initDirsend() < 0)
return(NULL);
addInputSource();
/* set the first timeout */
retryDirsend();
dirsendReturn = NULL;
dirsendDone = 0;
/* Until one of the callbacks says to return, keep processing events */
while (!dirsendDone)
processEvent();
/* Clean up event generators */
removeTimeOut();
removeInputSource();
#if defined(XARCHIE) || defined(VARCHIE)
/* Set status if needed (has to be outside of loop or X will crash) */
switch (dirsendDone) {
case DSRET_SEND_ERROR: status0("Send error"); break;
case DSRET_RECV_ERROR: status0("Recv error"); break;
case DSRET_TIMEOUT:
status1("Connection to %s timed out",to_hostname);
break;
case DSRET_ABORTED: status0("Aborted"); break;
}
#endif
/* Return whatever we're supposed to */
return(dirsendReturn);
}
/* - - - - - - - - */
/* This function does all the initialization that used to be done at the
* start of dirsend(), including opening the socket descriptor "lp". It
* returns the descriptor if successful, otherwise -1 to indicate that
* dirsend() should return NULL immediately.
*/
static int
initDirsend()
{
#if defined(XARCHIE) || defined(VARCHIE)
status0("Initializing");
#endif
if(one == 0) one = htons((short) 1);
priority = htons(rdgram_priority);
timeout.tv_sec = client_dirsrv_timeout;
timeout.tv_usec = 0;
ackwait.tv_sec = 0;
ackwait.tv_usec = 500000;
gapwait.tv_sec = (client_dirsrv_timeout < 5 ? client_dirsrv_timeout : 5);
gapwait.tv_usec = 0;
comp_thru = NULL;
perrno = 0;
nd_pkts = 0;
no_pkts = 0;
pkt_cid = 0;
/* Find first connection ID */
if(next_conn_id == 0) {
srand(getpid()+time(0)); /* XXX: arg ok, but not right type. */
next_conn_id = rand();
}
/* If necessary, find out what udp port to send to */
if (dir_udp_port == 0) {
register struct servent *sp;
tmp = pfs_enable; pfs_enable = PMAP_DISABLE;
#ifdef USE_ASSIGNED_PORT
/* UCX needs 0 & -1 */
sp = getservbyname("prospero","udp");
if (sp == (struct servent *)0 || sp == (struct servent *)-1) {
#ifdef DEBUG
if (pfs_debug)
fprintf(stderr, "dirsrv: udp/prospero unknown service - using %d\n",
PROSPERO_PORT);
#endif
dir_udp_port = htons((u_short) PROSPERO_PORT);
}
#else
/* UCX needs 0 & -1 */
sp = getservbyname("dirsrv","udp");
if (sp == (struct servent *)0 || sp == (struct servent *)-1) {
#ifdef DEBUG
if (pfs_debug)
fprintf(stderr, "dirsrv: udp/dirsrv unknown service - using %d\n",
DIRSRV_PORT);
#endif
dir_udp_port = htons((u_short) DIRSRV_PORT);
}
#endif
else dir_udp_port = sp->s_port;
pfs_enable = tmp;
#ifdef DEBUG
if (pfs_debug > 3)
fprintf(stderr,"dir_udp_port is %d\n", ntohs(dir_udp_port));
#endif
}
/* If we were given the host address, then use it. Otherwise */
/* lookup the hostname. If we were passed a host address of */
/* 0, we must lookup the host name, then replace the old value */
if(!hostaddr || (hostaddr->sin_addr.s_addr == 0)) {
/* I we have a null host name, return an error */
if((hostname == NULL) || (*hostname == '\0')) {
#ifdef DEBUG
if (pfs_debug)
fprintf(stderr, "dirsrv: Null hostname specified\n");
#endif
perrno = DIRSEND_BAD_HOSTNAME;
ptlfree(pkt);
/* return(NULL); */
return(-1);
}
/* If a port is included, save it away */
if(openparen = index(hostname,'(')) {
sscanf(openparen+1,"%d",&req_udp_port);
strncpy(hostnoport,hostname,400);
if((openparen - hostname) < 400) {
*(hostnoport + (openparen - hostname)) = '\0';
hostname = hostnoport;
}
}
#if defined(XARCHIE) || defined(VARCHIE)
status1("Getting address for host \"%s\"",hostname);
#endif
tmp = pfs_enable; pfs_enable = PMAP_DISABLE;
if((host = gethostbyname(hostname)) == NULL) {
pfs_enable = tmp;
/* Check if a numeric address */
newhostaddr = inet_addr(hostname);
if(newhostaddr == -1) {
#ifdef DEBUG
if (pfs_debug)
fprintf(stderr, "dirsrv: Can't resolve host %s\n",hostname);
#endif
perrno = DIRSEND_BAD_HOSTNAME;
ptlfree(pkt);
/* return(NULL); */
return(-1);
}
bzero((char *)&to, S_AD_SZ);
to.sin_family = AF_INET;
bcopy((char *) &newhostaddr, (char *)&to.sin_addr, 4);
if(hostaddr) bcopy(&to,hostaddr, S_AD_SZ);
}
else {
pfs_enable = tmp;
bzero((char *)&to, S_AD_SZ);
to.sin_family = host->h_addrtype;
#ifdef CUTCP
bcopy((char *) &host->h_addr, (char *)&to.sin_addr, host->h_length);
#else
bcopy(host->h_addr, (char *)&to.sin_addr, host->h_length);
#endif
if(hostaddr) bcopy(&to,hostaddr, S_AD_SZ);
}
}
else bcopy(hostaddr,&to, S_AD_SZ);
/* lmjm: Save away the hostname */
strncpy(to_hostname,inet_ntoa(to.sin_addr),sizeof(to_hostname)-1);
if(req_udp_port) to.sin_port = htons(req_udp_port);
else to.sin_port = dir_udp_port;
/* If a port was specified in hostaddr, use it, otherwise fill it in */
if(hostaddr) {
if(hostaddr->sin_port) to.sin_port = hostaddr->sin_port;
else hostaddr->sin_port = to.sin_port;
}
#ifndef CUTCP
/* Must open a new port each time. we do not want to see old */
/* responses to messages we are done with */
if ((lp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
#ifdef DEBUG
if (pfs_debug)
fprintf(stderr,"dirsrv: Can't open socket\n");
#endif
perrno = DIRSEND_UDP_CANT;
ptlfree(pkt);
/* return(NULL); */
return(-1);
}
#endif /* not CUTCP */
/* Try to bind it to a privileged port - loop through candidate */
/* ports trying to bind. If failed, that's OK, we will let the */
/* system assign a non-privileged port later */
#ifndef CUTCP
if(!notprived) {
for(tmp = PROS_FIRST_PRIVP; tmp < PROS_FIRST_PRIVP+PROS_NUM_PRIVP;
tmp++) {
#endif
bzero((char *)&us, sizeof(us));
us.sin_family = AF_INET;
#ifndef CUTCP
us.sin_port = htons((u_short) tmp);
if (bind(lp, (struct sockaddr *)&us, sizeof(us))) {
if(errno != EADDRINUSE) {
notprived++;
break;
}
}
else break;
}
}
#else
us.sin_port = htons(PROS_FIRST_PRIVP);
netulisten(PROS_FIRST_PRIVP);
#endif
#ifndef USE_V3_PROT
/* Add header */
if(rdgram_priority) {
pkt->start -= 15;
pkt->length += 15;
*(pkt->start) = (char) 15;
bzero(pkt->start+9,4);
*(pkt->start+11) = 0x02;
bcopy(&priority,pkt->start+13,2);
}
else {
pkt->start -= 9;
pkt->length += 9;
*(pkt->start) = (char) 9;
}
this_conn_id = htons(next_conn_id++);
if(next_conn_id == 0) next_conn_id++;
bcopy(&this_conn_id,pkt->start+1,2);
bcopy(&one,pkt->start+3,2);
bcopy(&one,pkt->start+5,2);
bzero(pkt->start+7,2);
#endif
#ifdef DEBUG
if (pfs_debug > 2) {
#ifndef USE_V3_PROT
if (to.sin_family == AF_INET) {
if(req_udp_port)
fprintf(stderr,"Sending message to %s+%d(%d)...",
to_hostname, req_udp_port, ntohs(this_conn_id));
else fprintf(stderr,"Sending message to %s(%d)...",
to_hostname, ntohs(this_conn_id));
}
#else
if (to.sin_family == AF_INET)
fprintf(stderr,"Sending message to %s...", to_hostname);
#endif /* USE_V3_PROT */
else
fprintf(stderr,"Sending message...");
(void) fflush(stderr);
}
#endif /* DEBUG */
first = ptalloc();
next = first;
#if defined(XARCHIE) || defined(VARCHIE)
status2("Connecting to %s (%s)",to_hostname,hostname);
packetCounter = 0;
#endif
#ifndef CUTCP
return(lp);
#else
return(1);
#endif /* CUTCP */
}
/* - - - - - - - - */
/*
* This used to be a label to goto to retry the last packet. Now we resend
* the packet and call keepWaitingDirsend() to wait for a reply. (We
* call keepWaitingDirsend() because formerly the code dropped through
* the keep_waiting label.)
*/
static void
retryDirsend()
{
#ifdef CUTCP
int lretry = 3;
#endif
gaps = ackpend = 0;
#ifndef CUTCP
ns = sendto(lp,(char *)(pkt->start), pkt->length, 0, (struct sockaddr *)&to, S_AD_SZ);
#else
while(--lretry) {
ns = netusend(&to.sin_addr,ntohs(to.sin_port),ntohs(us.sin_port),
(char *) pkt->start, pkt->length);
if(!ns)
break;
Stask();
Stask();
Stask();
}
#endif /* CUTCP */
#ifndef CUTCP
if(ns != pkt->length) {
#else
if(ns != 0) {
#endif
#ifdef DEBUG
if (pfs_debug) {
fprintf(stderr,"\nsent only %d/%d: ",ns, pkt->length);
perror("");
}
#endif
close(lp);
perrno = DIRSEND_NOT_ALL_SENT;
ptlfree(first);
ptlfree(pkt);
/* return(NULL); */
dirsendReturn = NULL;
dirsendDone = DSRET_SEND_ERROR;
}
#ifdef DEBUG
if (pfs_debug > 2) fprintf(stderr,"Sent.\n");
#endif
keepWaitingDirsend();
}
/* - - - - - - - - */
/*
* This used to be a label to goto to set the appropriate timeout value
* and blocked in select(). Now we set selwait and the fd_sets to the
* appropriate values, and in X register a new timeout, then return to
* allow event processing.
*/
static void
keepWaitingDirsend()
{
/* We come back to this point (by a goto) if the packet */
/* received is only part of the response, or if the */
/* response came from the wrong host */
#ifdef DEBUG
if (pfs_debug > 2) fprintf(stderr,"Waiting for reply...");
#endif
#ifndef CUTCP
FD_ZERO(&readfds);
FD_SET(lp, &readfds);
#endif
if(ackpend) selwait = &ackwait;
else if(gaps) selwait = &gapwait;
else selwait = &timeout;
addTimeOut();
}
/* - - - - - - - - */
/*
* This routine is called when a timeout occurs. It includes the code that
* was formerly used when select() returned 0 (indicating a timeout).
*/
/*ARGSUSED*/
static void
timeoutProc(client_data,id)
XtPointer client_data;
XtIntervalId *id;
{
if (gaps || ackpend) { /* Send acknowledgment */
/* Acks are piggybacked on retries - If we have received */
/* an ack from the server, then the packet sent is only */
/* an ack and the rest of the message will be empty */
#ifdef DEBUG
if (pfs_debug > 2) {
fprintf(stderr,"Acknowledging (%s).\n",
(ackpend ? "requested" : "gaps"));
}
#endif
retryDirsend();
return;
}
if (retries-- > 0) {
timeout.tv_sec = CLIENT_DIRSRV_BACKOFF(timeout.tv_sec);
#ifdef DEBUG
if (pfs_debug > 2) {
fprintf(stderr,"Timed out. Setting timeout to %d seconds.\n",
timeout.tv_sec);
}
#endif
#if defined(XARCHIE) || defined(VARCHIE)
status1("Timed out -- retrying (%d seconds)",timeout.tv_sec);
#endif
retryDirsend();
return;
}
#ifdef DEBUG
if (pfs_debug) {
fprintf(stderr, "select failed(timeoutProc): readfds=%x ",
readfds);
perror("");
}
#endif
#ifndef CUTCP
close(lp);
#endif
perrno = DIRSEND_SELECT_FAILED;
ptlfree(first);
ptlfree(pkt);
/* return(NULL); */
dirsendReturn = NULL;
dirsendDone = DSRET_TIMEOUT;
}
/* - - - - - - - - */
/*
* This function is called whenever there's something to read on the
* connection. It includes the code that was run when select() returned
* greater than 0 (indicating read ready).
*/
/*ARGSUSED*/
static void
readProc(client_data,source,id)
XtPointer client_data;
int *source;
XtInputId *id;
{
#ifdef CUTCP
int lretry = 3;
#endif
/* We got something to read, so clear the timer */
removeTimeOut();
from_sz = sizeof(from);
next->start = next->dat;
#ifndef CUTCP
if ((nr = recvfrom(lp, next->start, sizeof(next->dat), 0, (struct sockaddr *)&from, &from_sz)) < 0) {
#else
nr = neturead(next->start);
if (nr < 1) {
#endif
#ifdef DEBUG
if (pfs_debug) perror("recvfrom");
#endif
#ifndef CUTCP
close(lp);
#endif
perrno = DIRSEND_BAD_RECV;
ptlfree(first);
ptlfree(pkt);
/* return(NULL) */
dirsendReturn = NULL;
dirsendDone = DSRET_RECV_ERROR;
return;
}
next->length = nr;
next->start[next->length] = 0;
#ifdef DEBUG
if (pfs_debug > 2)
fprintf(stderr,"Received packet from %s\n",inet_ntoa(from.sin_addr));
#endif
#if defined(XARCHIE) || defined(VARCHIE)
if (packetCounter == 0)
status2("Connected to %s (%s)",to_hostname,hostname);
else
status1("Receiving...%d",packetCounter);
packetCounter += 1;
#endif
/* For the current format, if the first byte is less than */
/* 20, then the first two bits are a version number and the next six */
/* are the header length (including the first byte). */
if((hdr_len = (unsigned char) *(next->start)) < 20) {
ctlptr = next->start + 1;
next->seq = 0;
if(hdr_len >= 3) { /* Connection ID */
bcopy(ctlptr,&stmp,2);
if(stmp) pkt_cid = ntohs(stmp);
ctlptr += 2;
}
/*
* Problem noted by eanders+@cmu.edu against the V5 Prospero server:
* "the problem is that clients look at a unsigned short as a signed
* integer. Then they do comparisons, and naturally they are not
* equal." This fix is as opposed to some kind of casting, and
* will be moot when the new clients are written for the new servers.
*/
if (pkt_cid < 0)
pkt_cid = 65536+pkt_cid;
if(pkt_cid && this_conn_id && (pkt_cid != ntohs(this_conn_id))) {
/* The packet is not for us */
/* goto keep_waiting; */
#ifdef DEBUG
if (pfs_debug > 20)
fprintf(stderr,"Packet not for us %d,%d,%d\n",
pkt_cid,this_conn_id,ntohs(this_conn_id));
#endif
keepWaitingDirsend();
return;
}
if(hdr_len >= 5) { /* Packet number */
bcopy(ctlptr,&stmp,2);
next->seq = ntohs(stmp);
ctlptr += 2;
}
else { /* No packet number specified, so this is the only one */
next->seq = 1;
nd_pkts = 1;
}
if(hdr_len >= 7) { /* Total number of packets */
bcopy(ctlptr,&stmp,2); /* 0 means don't know */
if(stmp) nd_pkts = ntohs(stmp);
ctlptr += 2;
}
if(hdr_len >= 9) { /* Receievd through */
bcopy(ctlptr,&stmp,2); /* 1 means received request */
#ifndef USE_V3_PROT
if((stmp) && (ntohs(stmp) == 1)) {
/* Future retries will be acks only */
pkt->length = 9;
bcopy(&zero,pkt->start+3,2);
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Server acked request - retries will be acks only\n");
#endif
}
#endif
ctlptr += 2;
}
if(hdr_len >= 11) { /* Backoff */
bcopy(ctlptr,&stmp,2);
if(stmp) {
backoff = (short)ntohs(stmp);
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Backing off to %d seconds\n", backoff);
#endif
timeout.tv_sec = backoff;
if ((backoff > 60) && (first == next) && (no_pkts == 0)) {
/* Probably a long queue on the server - don't give up */
retries = client_dirsrv_retry;
}
}
ctlptr += 2;
}
if(hdr_len >= 12) { /* Flags (1st byte) */
bcopy(ctlptr,&rdflag11,1);
if(rdflag11 & 0x80) {
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Ack requested\n");
#endif
ackpend++;
}
if(rdflag11 & 0x40) {
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Sequenced control packet\n");
#endif
next->length = -1;
scpflag++;
}
ctlptr += 1;
}
if(hdr_len >= 13) { /* Flags (2nd byte) */
/* Reserved for future use */
bcopy(ctlptr,&rdflag12,1);
ctlptr += 1;
}
if(next->seq == 0) {
/* goto keep_waiting; */
keepWaitingDirsend();
return;
}
if(next->length >= 0) next->length -= hdr_len;
next->start += hdr_len;
goto done_old;
}
pkt_cid = 0;
/* if intermediate format (between old and new), then process */
/* and go to done_old */
ctlptr = next->start + max(0,next->length-20);
while(*ctlptr) ctlptr++;
/* Control fields start after the terminating null */
ctlptr++;
/* Until old version are gone, must be 4 extra bytes minimum */
/* When no version 3 servers, can remove the -4 */
if(ctlptr < (next->start + next->length - 4)) {
/* Connection ID */
bcopy(ctlptr,&stmp,2);
if(stmp) pkt_cid = ntohs(stmp);
ctlptr += 2;
if(pkt_cid && this_conn_id && (pkt_cid != ntohs(this_conn_id))) {
/* The packet is not for us */
/* goto keep_waiting; */
keepWaitingDirsend();
return;
}
/* Packet number */
if(ctlptr < (next->start + next->length)) {
bcopy(ctlptr,&stmp,2);
next->seq = ntohs(stmp);
ctlptr += 2;
}
/* Total number of packets */
if(ctlptr < (next->start + next->length)) {
bcopy(ctlptr,&stmp,2);
if(stmp) nd_pkts = ntohs(stmp);
ctlptr += 2;
}
/* Receievd through */
if(ctlptr < (next->start + next->length)) {
/* Not supported by clients */
ctlptr += 2;
}
/* Backoff */
if(ctlptr < (next->start + next->length)) {
bcopy(ctlptr,&stmp,2);
backoff = ntohs(stmp);
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Backing off to %d seconds\n", backoff);
#endif
if(backoff) timeout.tv_sec = backoff;
ctlptr += 2;
}
if(next->seq == 0) {
/* goto keep_waiting; */
keepWaitingDirsend();
return;
}
goto done_old;
}
/* Notes that we have to start searching 11 bytes before the */
/* expected start of the MULTI-PACKET line because the message */
/* might include up to 10 bytes of data after the trailing null */
/* The order of those bytes is two bytes each for Connection ID */
/* Packet-no, of, Received-through, Backoff */
seqtxt = nlsindex(next->start + max(0,next->length - 40),"MULTI-PACKET");
if(seqtxt) seqtxt+= 13;
if((nd_pkts == 0) && (no_pkts == 0) && (seqtxt == NULL)) goto all_done;
tmp = sscanf(seqtxt,"%d OF %d", &(next->seq), &nd_pkts);
#ifdef DEBUG
if (pfs_debug && (tmp == 0))
fprintf(stderr,"Cant read packet sequence number: %s", seqtxt);
#endif
done_old:
#ifdef DEBUG
if(pfs_debug > 2) fprintf(stderr,"Packet %d of %d\n",next->seq,nd_pkts);
#endif
if ((first == next) && (no_pkts == 0)) {
if(first->seq == 1) {
comp_thru = first;
/* If only one packet, then return it */
if(nd_pkts == 1) goto all_done;
}
else gaps++;
no_pkts = 1;
next = ptalloc();
/* goto keep_waiting; */
keepWaitingDirsend();
return;
}
if(comp_thru && (next->seq <= comp_thru->seq))
ptfree(next);
else if (next->seq < first->seq) {
vtmp = first;
first = next;
first->next = vtmp;
first->previous = NULL;
vtmp->previous = first;
if(first->seq == 1) comp_thru = first;
no_pkts++;
}
else {
vtmp = (comp_thru ? comp_thru : first);
while (vtmp->seq < next->seq) {
if(vtmp->next == NULL) {
vtmp->next = next;
next->previous = vtmp;
next->next = NULL;
no_pkts++;
goto ins_done;
}
vtmp = vtmp->next;
}
if(vtmp->seq == next->seq)
ptfree(next);
else {
vtmp->previous->next = next;
next->previous = vtmp->previous;
next->next = vtmp;
vtmp->previous = next;
no_pkts++;
}
}
ins_done:
while(comp_thru && comp_thru->next &&
(comp_thru->next->seq == (comp_thru->seq + 1))) {
comp_thru = comp_thru->next;
#ifndef USE_V3_PROT
recvd_thru = htons(comp_thru->seq);
bcopy(&recvd_thru,pkt->start+7,2); /* Let server know we got it */
#endif
/* We've made progress, so reset retry count */
retries = client_dirsrv_retry;
/* Also, next retry will be only an acknowledgement */
/* but for now, we can't fill in the ack field */
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Packets now received through %d\n",comp_thru->seq);
#endif
}
/* See if there are any gaps */
if(!comp_thru || comp_thru->next) gaps++;
else gaps = 0;
if ((nd_pkts == 0) || (no_pkts < nd_pkts)) {
next = ptalloc();
/* goto keep_waiting; */
keepWaitingDirsend();
return;
}
all_done:
if(ackpend) { /* Send acknowledgement if requested */
#ifdef DEBUG
if (pfs_debug > 2) {
if (to.sin_family == AF_INET)
fprintf(stderr,"Acknowledging final packet to %s(%d)\n",
to_hostname, ntohs(this_conn_id));
else
fprintf(stderr,"Acknowledging final packet\n");
(void) fflush(stderr);
}
#endif
#ifndef CUTCP
ns = sendto(lp,(char *)(pkt->start), pkt->length, 0, (struct sockaddr *)&to, S_AD_SZ);
#else
while(--lretry) {
ns = netusend(&to.sin_addr, ntohs(to.sin_port), ntohs(us.sin_port),(char *) pkt->start, pkt->length);
if(!ns)
break;
Stask();
Stask();
}
#endif
#ifndef CUTCP
if(ns != pkt->length) {
#else
if(ns != 0) {
#endif
#ifdef DEBUG
if (pfs_debug) {
fprintf(stderr,"\nsent only %d/%d: ",ns, pkt->length);
perror("");
}
#endif
}
}
#ifndef CUTCP
close(lp);
#endif
ptlfree(pkt);
/* Get rid of any sequenced control packets */
if(scpflag) {
while(first && (first->length < 0)) {
vtmp = first;
first = first->next;
if(first) first->previous = NULL;
ptfree(vtmp);
}
vtmp = first;
while(vtmp && vtmp->next) {
if(vtmp->next->length < 0) {
if(vtmp->next->next) {
vtmp->next = vtmp->next->next;
ptfree(vtmp->next->previous);
vtmp->next->previous = vtmp;
}
else {
ptfree(vtmp->next);
vtmp->next = NULL;
}
}
vtmp = vtmp->next;
}
}
/* return(first); */
dirsendReturn = first;
dirsendDone = DSRET_DONE;
}
/* - - - - - - - - */
/* These routines allow dirsend() to run with or without X by providing
* wrappers around the calls that handle the asynchronous communication.
* All parameters are passed using globals.
* Under X: The input sources and timeouts are set using Xt calls, and
* processEvent() just calls XtAppProcessEvent().
* Non-X: None of the input sources and timeouts are used, and
* processEvent() calls select() to handle both timeouts and the
* socket file descriptor. The return value of select() is used
* to determine which callback routine to call.
*/
static void
addInputSource()
{
#ifdef XARCHIE
inputId = XtAppAddInput(appContext,lp,(XtPointer)XtInputReadMask,
readProc,NULL);
#endif
}
static void
removeInputSource()
{
#ifdef XARCHIE
XtRemoveInput(inputId);
#endif
}
static void
addTimeOut()
{
#ifdef XARCHIE
unsigned long timeoutLen = selwait->tv_sec*1000 + selwait->tv_usec/1000;
/* old timeout can still be there if we are being called after the
* file descriptor was read, so we remove it just to be sure. */
removeTimeOut();
timerId = XtAppAddTimeOut(appContext,timeoutLen,timeoutProc,NULL);
#endif
}
static void
removeTimeOut()
{
#ifdef XARCHIE
if (timerId != (XtIntervalId)0) {
XtRemoveTimeOut(timerId);
timerId = (XtIntervalId)0;
}
#endif
}
/*
* In X, this just calls the X routine that blocks waiting for an event,
* timer, or input source and dispatches it.
*
* Otherwise, for Unix we call select() with the appropriate arguments,
* and act on its return calue as follows:
* == 0 : The timer expired, call timeoutProc() then return to, presumably,
* the loop that calling processEvent() until dirsendDone.
* < 0 : If we were interrupted (errno == EINTR) then don't do anything.
* Presumably the signal handler set flags if needed, and we'll
* come back to select() again on the next pass of the outer loop
* if we're supposed to. Otherwise, puke on the error.
* > 0 : The socket is ready for reading, so call readProc().
*
* Otherwise, if we're in MSDOS (CUTCP defined) then I (gf) have no idea
* what's going on. Ask Brendan.
*/
static void
processEvent()
{
#ifdef CUTCP
unsigned long now;
#endif
#ifdef XARCHIE
XtAppProcessEvent(appContext,XtIMAll);
#else
/* select - either recv is ready, or timeout */
/* see if timeout or error or wrong descriptor */
#ifndef CUTCP
tmp = select(lp + 1, &readfds, (fd_set *)0, (fd_set *)0, selwait);
if (tmp == 0) {
timeoutProc(NULL,&timerId);
} else if (tmp < 0 && errno == EINTR) { /* gf: new for ^C in varchie */
#ifdef DEBUG
fprintf(stderr,"select interrupted\n"); /* do nothing, we'll be back */
#endif
} else if ((tmp < 0) || !FD_ISSET(lp,&readfds)) {
#ifdef DEBUG
if (pfs_debug) {
fprintf(stderr, "select failed(processEvent): readfds=%x ",
readfds);
perror("");
}
#endif
close(lp);
#else /* CUTCP's flood. */
/* while not timeout in selwait loop, stask looking for uevents */
now = time(NULL) + selwait->tv_sec;
#ifdef DEBUG
if(pfs_debug) {
fprintf(stderr,"Waiting %d seconds\n",selwait->tv_sec);
}
#endif
while(now > time(NULL)) {
int i, cl, dat;
Stask();
if (0 < (i = Sgetevent(USERCLASS, &cl, &dat))) {
/* got a user class event */
if(cl == USERCLASS &&
i == UDPDATA) {
readProc(NULL,&lp,&inputId);
return;
}
}
if(kbhit()) {
int c = getch();
if(c == 27 || c == 3)
break;
fprintf(stderr,"Press <ESCAPE> to abort\n");
}
}
if(now <= time(NULL)) { /* timeout */
timeoutProc(NULL,&timerId);
return;
}
#endif /* CUTCP */
perrno = DIRSEND_SELECT_FAILED;
ptlfree(first);
ptlfree(pkt);
/* return(NULL); */
dirsendReturn = NULL;
dirsendDone = DSRET_SELECT_ERROR;
#ifndef CUTCP
} else {
readProc(NULL,&lp,&inputId);
}
#endif /* CUTCP */
#endif /* XARCHIE */
}
void
abortDirsend()
{
if (!dirsendDone) {
#ifndef CUTCP
close(lp);
#endif
ptlfree(first);
ptlfree(pkt);
dirsendReturn = NULL;
dirsendDone = DSRET_ABORTED;
}
return;
}